package com.gxa.hotel.portal.service.impl;


import com.gxa.hotel.core.exception.BusinessException;
import com.gxa.hotel.core.exception.ExceptionCode;
import com.gxa.hotel.portal.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 问题:
 * 1.发生了高并发请求中,由于请求太快,而业务操作还未完成,导致判断(>0)失败
 * 2.能否用java的同步锁(Sync?)不能,原因是锁只能在当前服务器中生效,服务器是集群,锁无法锁住所有请求
 * 3.如何让锁锁住所有请求?redis创建,通过NX机制创建,业务请求中,先获得锁,再请求业务
 * 4.如何保证在异常的情况
 */
@Service
public class GoodsServiceImpl implements GoodsService {

    //准备redis操作对象
    @Autowired
    private RedisTemplate<String,Object> redisTemplate=null;

    private static final String SEC_KILL="seckill:";
    private static final String LOCK="LOCK:";

    @Override
    public Boolean seckill(Long id) throws BusinessException {
        //当前需要获取用户的时候,用 UserThreadLocal

        ValueOperations<String, Object> stringPos = redisTemplate.opsForValue();

        //为了包装只有当前操作线程才能是否锁
        //为此,声明特殊的值,保存在当前线程中
        String value = UUID.randomUUID().toString().replace("-","");

        //加锁 redis name value nx
        Boolean lockSuccess = stringPos.setIfAbsent(LOCK + id, value, 5, TimeUnit.SECONDS);

        //如果加锁成功,才能执行后面的过程
        if (!lockSuccess){
            return false;
        }
        //从redis中获取秒杀商品的数量
        //声明表示该用户加锁
        Object num = stringPos.get(SEC_KILL + id);

        //数据校验
        if (StringUtils.isEmpty(num)){
            throw new BusinessException(ExceptionCode.NULL_GOODS_ID);
        }
        //判断是否>0 不满足条件,直接返回false表示秒杀失败
       if (Integer.parseInt(num.toString())<=0){
           return false;
       }
        //如果满足条件,就让秒杀商品的数量-1

        //库存-1后,
        //还需要释放
        //判断当前锁的状态,value是否和加锁的时候UUID是一致
        if (stringPos.get(LOCK+id).equals(value)){
            stringPos.decrement(SEC_KILL+id);
            redisTemplate.delete(LOCK+id);
        }

        System.out.println("秒杀成功,商品的库存还剩:"+(Integer.parseInt(num.toString())-1));
        //库存-1后,返回true,表示该用户秒杀成功


        return true;
    }


}
